#include "spreadsheet.h"
#include "cell.h"
#include "spreadsheetcompare.h"
#include <QDebug>
#include <QString>
#include <QFile>
#include <QApplication>
#include <QMessageBox>
#include <QClipboard>
#include <QList>

SpreadSheet::SpreadSheet(QWidget *parent) :
    QTableWidget(parent)
{
    autoRecalc = true;

    // 指定 tableWidget的Item的类型，
    // 当必要的时候调用clone函数新建一个table item
    setItemPrototype(new Cell);
    // 设置tableWidget的选择模型
    // QAbstractItemView::ContiguousSelection
    // --> 当一个item被选择时， 选择另一个item,前一个item取消选中
    //     选中一个item, 按shift选中另一个item, 将选中这两个item确定的区域
    setSelectionMode(ContiguousSelection);
    // 当item的数据发生改变的时候，发送itemChanged信号，
    // 连接到槽函数somethingChanged
    connect(this, SIGNAL(itemChanged(QTableWidgetItem*)),
            this, SLOT(somethingChanged()));

    // 重置tableWidget
    // 设置行,列数 并且设置类标题(A-Z)，将当前cell移动到(0,0)位置
    clear();
}

// 获取当前item的位置  (A, 1)为第一个
QString SpreadSheet::currentLocation() const
{
    return QChar('A'+currentColumn()) + QString::number(currentRow()+1);
}

// 获取当前item中的公式
QString SpreadSheet::currentFormula() const
{
    return formula(currentRow(), currentColumn());
}

//
QTableWidgetSelectionRange SpreadSheet::selectedRange() const
{
    // 获取tableWidget中的所有选区
    QList<QTableWidgetSelectionRange> ranges = selectedRanges();
    // 没有选中的选区，返回一个空的tableWidgetSelectionItem
    if (ranges.isEmpty())
        return QTableWidgetSelectionRange();
    // 返回选区第一个元素的引用 (ranges must not be empty).
    return ranges.first();
}

void SpreadSheet::clear()
{
    // 重置tableWidget的行列数，先清空(set columns and rows equal 0)
    // 然后设定默认的rows, columns
    setRowCount(0);
    setColumnCount(0);
    setRowCount(RowCount);
    setColumnCount(ColumCount);

    for (int i = 0; i < ColumCount; ++i){
        QTableWidgetItem *item = new QTableWidgetItem;
        // 设置item的文本内容
        item->setText(QString(QChar('A'+i)));
        // 设置类标题中地i列的元素
        setHorizontalHeaderItem(i, item);
    }
    //依据当前的 selection mode, 设置当前cell位置， 可能处于选中的状态
    setCurrentCell(0,0);
}

// 读取文件
bool SpreadSheet::readFile(const QString &fileName)
{
    QFile file(fileName);

    // 只读方式打开文件
    if (!file.open(QIODevice::ReadOnly)){
        QMessageBox::warning(this, "SpreadSheet",
                             QString("Cann't read file %1: %2")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        //file.errorString() 获取上一次发生错误的信息(type: QString)
        return false;
    }

    // 创建一个绑定到文件的数据流
    QDataStream in(&file);
    // 设置数据流的版本格式
    in.setVersion(QDataStream::Qt_4_8);
    // 读取magic number, 用于判断是否是正确的文件类型
    qint32 magic;
    in >> magic;

    if (magic != MagicNumber){
        QMessageBox::warning(this, "SpreadSheet",
                             QString("The file is not a SpreadSheet file"));
        return false;
    }
    clear();  // 重置 tableWidget
    // quint16 是平台无关的， 确保是16位数据
    quint16 row;
    quint16 column;
    QString str;
    // 设置鼠标状态为： Qt::WaitCursor --> 等待状态
    QApplication::setOverrideCursor(Qt::WaitCursor);
    while (!in.atEnd()){
        // 读取行列，以及数据
        in >> row >> column >> str;
        // 设置单元格数据， (有公式的设置为公式的格式)
        setFormula(row, column, str);
    }
    // 恢复鼠标的状态
    QApplication::restoreOverrideCursor();
    return true;
}

// 写入文件
bool SpreadSheet::writeFile(const QString &fileName)
{
    // QIODevice::WirteOnly方式写入
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly)){
        QMessageBox::warning(this, "SpreadSheet",
                             QString("can't write file %1:\n%2")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }

    // out数据流绑定到file上
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_4_8);
    // 先写入用于版本控制的magicNumber
    out << quint32(MagicNumber);
    // 光标改为 WaitCursor
    QApplication::setOverrideCursor(Qt::WaitCursor);
    // 遍历整张表，写入数据（有公式的单元格，写入公式）
    for (int row = 0; row < RowCount; ++row){
        for (int column = 0; column < ColumCount; ++column){
            QString str = formula(row, column);
            if (!str.isEmpty())
                out << quint16(row) << quint16(column) << str;
        }
    }
    // 恢复原先的光标形态
    QApplication::restoreOverrideCursor();
    return true;
}


// 排序
void SpreadSheet::sort(const SpreadsheetCompare &compare)
{
    QList<QStringList> rows;
    QTableWidgetSelectionRange range = selectedRange();
    int i;
    for (i = 0; i < range.rowCount(); ++i){
        QStringList row;
        for (int j = 0; j < range.columnCount(); ++j)
            row.append(formula(range.topRow() + i,
                               range.leftColumn() + j));
        rows.append(row);
    }
    qStableSort(rows.begin(), rows.end(), compare);
    for (i = 0; i < range.rowCount(); ++i){
        for (int j = 0; j < range.columnCount(); ++j)
            setFormula(range.topRow() + i, range.leftColumn() + j,
                       rows[i][j]);
    }
    clearSelection();
    somethingChanged();
}

void SpreadSheet::cut()
{
    copy();
    del();
}

void SpreadSheet::copy()
{
    // 获取第一个选中的区域
    QTableWidgetSelectionRange range = selectedRange();
    QString str;

    for (int i = 0; i < range.rowCount(); ++i){
        if (i > 0)
            str += "\n";
        for (int j = 0; j < range.columnCount(); ++j){
            if (j > 0)
                str += "\t";
            str += formula(range.topRow()+i, range.leftColumn() + j);
        }
    }
    QApplication::clipboard()->setText(str);
}

void SpreadSheet::paste()
{
    QTableWidgetSelectionRange range = selectedRange();
    QString str = QApplication::clipboard()->text();
    QStringList rows = str.split('\n');
    int numRows = rows.count();
    int numColumns = rows.first().count('\t') + 1;

    if (range.rowCount() * range.columnCount() != 1
            && (range.rowCount() != numRows
                || range.columnCount() != numColumns)){
        QMessageBox::information(this, "SpreadSheet",
                                 "The information cann't be paste because the copy"
                                 "and paste areas aren't the same size");
        return;
    }

    for (int i = 0; i < numRows; ++i){
        QStringList colmuns = rows[i].split('\t');
        for (int j = 0; j < numColumns; ++j){
            int row = range.topRow() + i;
            int column = range.leftColumn() + j;
            if (row < RowCount && column < ColumCount)
                setFormula(row, column, colmuns[j]);
        }
    }
    somethingChanged();
}

void SpreadSheet::del()
{
    QList<QTableWidgetItem *> items = selectedItems();
    if (!items.isEmpty()){
        foreach (QTableWidgetItem *item, items)
            delete item;
        somethingChanged();
    }
}

void SpreadSheet::selectCurrentRow()
{
    selectRow(currentRow());
}

void SpreadSheet::selectCurrentColumn()
{
    selectColumn(currentColumn());
}

void SpreadSheet::recalculate()
{
    for (int row = 0; row < RowCount; ++row){
        for (int column = 0; column < ColumCount; ++column){
            if (cell(row, column))
                cell(row, column)->setDirty();
        }
    }
    viewport()->update();
}

void SpreadSheet::setAutoRecalculate(bool recalc)
{
    autoRecalc = recalc;
    if (autoRecalc)
        recalculate();
}

void SpreadSheet::findNext(const QString &str, Qt::CaseSensitivity cs)
{
    int row = currentRow();
    int column = currentColumn() + 1;
    while (row < RowCount){
        while (column < ColumCount){
            if (text(row, column).contains(str, cs)){
                clearSelection();
                setCurrentCell(row, column);
                activateWindow();
                return;
            }
            ++column;
        }
        column = 0;
        ++row;
    }
    QApplication::beep();
}

void SpreadSheet::findPrevious(const QString &str, Qt::CaseSensitivity cs)
{
    int row = currentRow();
    int column = currentColumn() - 1;
    while (row >= 0){
        while (column >= 0){
            if (text(row, column).contains(str, cs)){
                clearSelection();
                setCurrentCell(row, column);
                activateWindow();
                return;
            }
            --column;
        }
        column = ColumCount -1;
        --row;
    }
    QApplication::beep();
}

void SpreadSheet::somethingChanged()
{
    if (autoRecalc)
        recalculate();
    emit modified();
}

Cell *SpreadSheet::cell(int row, int column) const
{
    return static_cast<Cell *>(item(row, column));
}

QString SpreadSheet::text(int row, int column) const
{
    Cell *c= cell(row, column);
    if (c){
        return c->text();
    }else{
        return "";
    }
}

QString SpreadSheet::formula(int row, int column) const
{
    Cell *c= cell(row, column);
    if (c){
        return c->text();
    }else{
        return "";
    }
}

void SpreadSheet::setFormula(int row, int column, const QString &formula)
{
    Cell* c = cell(row, column);
    if (!c){
        c = new Cell;
        setItem(row, column, c);
    }
    c->setFormula(formula);
}
